#include"generators.h"
#include"get_editor_distance.h"
#include"get_editor_script.h"
#include"string_conversation.h"
#include"check.h"
#include<algorithm>
#include<stdexcept>
#include<string>
#include<iostream>
#include<fstream>

template<typename T>
void checkEditorCorrectness(const PackOfIterators<typename std::vector<T>::iterator>& boundOfContainers,
	typename std::vector<EditorState<T> >::iterator scriptBegin,
	typename std::vector<EditorState<T> >::iterator scriptEnd)
{
	int firstSize = boundOfContainers.firstEnd - boundOfContainers.firstBegin;
	int secondSize = boundOfContainers.secondEnd - boundOfContainers.secondBegin;
	int expectedEditorDistance = getEditorDistance(boundOfContainers);
	int position1 = 0;
	int position2 = 0;
	int sequenceSize = scriptEnd - scriptBegin;
	int foundEditorDistance = 0;
	for (auto positionInScript = scriptBegin; positionInScript != scriptEnd; ++positionInScript){
		if (positionInScript->signOfState == SAME){
			if (position1 < firstSize && position2 < secondSize){
				if (*(boundOfContainers.firstBegin + position1) == *(boundOfContainers.secondBegin + position2)
					&& *(boundOfContainers.firstBegin + position1) == positionInScript->stringOfState){
					position1++;
					position2++;
				}
				else{
					throw std::runtime_error("Wrong recovering");
				}
			}
			else{
				throw std::runtime_error("Out of range error");
			}
		}
		else if (positionInScript->signOfState == DELETED){
			if (position1 < firstSize
				&& (*(boundOfContainers.firstBegin + position1)) == positionInScript->stringOfState){
				position1++;
				foundEditorDistance++;
			}
			else{
				throw std::runtime_error("There isn't -");
			}
		}
		else{
			if (position2 < secondSize
				&& (*(boundOfContainers.secondBegin + position2)) == positionInScript->stringOfState){
				position2++;
				foundEditorDistance++;
			}
			else
				throw std::runtime_error("There isn't +");
		}
	}
	if (foundEditorDistance != expectedEditorDistance)
		throw std::runtime_error("Difference is wrong");
}
void unitSignedArrayTest(){
	std::vector<long long> values(201);
	SignedArray<long long> newSignedArray(201,100);
	for (int i = -100; i <= 100; ++i){
		long long currentValue = generate_number();
		values[i + 100] = currentValue;
		newSignedArray[i] = currentValue;
	}
	for (int i = -100; i <= 100; ++i){
		if (values[i + 100] != newSignedArray[i]){
			throw std::runtime_error("Signed array doesn't work correctly\n");
		}
	}
}

void unitTestPrefixes(){
	{
		std::vector<long long> sequence1{ 1 };
		std::vector<long long> sequence2;
		if (maxPrefix(PackOfIterators<std::vector<long long>::iterator>
			{ sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end() }) != 0){
			
			throw std::runtime_error("The longest prefix isn't correct");
		}
	}

	{
		std::vector<long long> sequence1{ 1 };
		std::vector<long long> sequence2{ 1, 2 };
		if (maxPrefix(PackOfIterators<std::vector<long long>::iterator>
			{ sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end() }) != 1){
			
			throw std::runtime_error("The longest prefix isn't correct");
		}
	}
	{
		std::vector<long long> sequence1{ 1, 3 };
		std::vector<long long> sequence2{ 1, 2 };
		if (maxPrefix(PackOfIterators<std::vector<long long>::iterator>
			{ sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end() }) != 1){
			
			throw std::runtime_error("The longest prefix isn't correct");
		}
	}
	{
		std::vector<long long> sequence1{ 1, 2 };
		std::vector<long long> sequence2{ 1, 2 };
		if (maxPrefix(PackOfIterators<std::vector<long long>::iterator>
			{ sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end() }) != 2){
			
			throw std::runtime_error("The longest prefix isn't correct");
		}
	}
	{
		std::vector<long long> sequence1{ 1, 2, 1999999, 23, 45, 54 };
		std::vector<long long> sequence2{ 1, 2, 1999999, 46, 43 };
		if (maxPrefix(PackOfIterators<std::vector<long long>::iterator>
			{ sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end() }) != 3){
			
			throw std::runtime_error("The longest prefix isn't correct");
		}
	}
	
}
void checkingMaxPrefixesCorrectness(){
	for (int tests = 1; tests <= TEST_VALUES; ++tests){
		std::cerr << tests << std::endl;
		std::vector<long long> sequence1 = generate_sequence();
		std::vector<long long> sequence2 = spoil_some_numbers_with_delete(sequence1);
		int foundMaximalPrefix = maxPrefix(PackOfIterators<std::vector<long long>::iterator>
			{ sequence1.begin(), sequence1.end(), 	sequence2.begin(), sequence2.end() });
		if (foundMaximalPrefix > sequence1.size() || foundMaximalPrefix > sequence2.size()){
			
			throw std::runtime_error("Out of range error");
		}

		for (int i = 0; i < foundMaximalPrefix; ++i){
			if (sequence1[i] != sequence2[i]){
				
				throw std::runtime_error("The longest prefix is less than required");
			}
		}
		if (foundMaximalPrefix < sequence1.size() && foundMaximalPrefix < sequence2.size() &&
			sequence1[foundMaximalPrefix] == sequence2[foundMaximalPrefix]){
			
			throw std::runtime_error("The longest prefix is more than required");
		}

	}
	
}


void unitTestsLCS(){
	{
		std::vector<long long> sequence1{ 1 };
		std::vector<long long> sequence2;
		if (getEditorDistance(PackOfIterators<std::vector<long long>::iterator>
			{ sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end() }) != 1){
			
			throw std::runtime_error("Wrong editor distance");
		}
	}
	{
	std::vector<long long> sequence1{ 1 };
	std::vector<long long> sequence2{ 2 };
	if (getEditorDistance(PackOfIterators<std::vector<long long>::iterator>
		{ sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end() }) != 2){
		
		throw std::runtime_error("Wrong editor distance");
	}
	}
	{
		std::vector<long long> sequence1{ 1, 2 };
		std::vector<long long> sequence2{ 2 };
		if (getEditorDistance(PackOfIterators<std::vector<long long>::iterator>
			{ sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end() }) != 1){
			
			throw std::runtime_error("Wrong editor distance");
		}
	}
	{
		std::vector<long long> sequence1{ 1, 2, 3 };
		std::vector<long long> sequence2{ 1 };
		if (getEditorDistance(PackOfIterators<std::vector<long long>::iterator>
			{ sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end() }) != 2){

			throw std::runtime_error("Wrong editor distance");
		}
	}
	{
		std::string sequence1 = "a";
		std::string sequence2 = "abc";
		if (getEditorDistance(PackOfIterators<std::string::iterator>
			{ sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end() }) != 2){
			throw std::runtime_error("Wrong editor distance");
		}
	}
	{
		std::vector<long long> sequence1{ 1, 2 };
		std::vector<long long> sequence2{ 2, 3, 4 };
		if (getEditorDistance(PackOfIterators<std::vector<long long>::iterator>
			{ sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end() }) != 3){
			throw std::runtime_error("Wrong editor distance");
		}
	}
	{
		std::vector<long long> sequence1{ 2,3,5,1 };
		std::vector<long long> sequence2{ 2, 3, 4 };
		if (getEditorDistance(PackOfIterators<std::vector<long long>::iterator>
			{ sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end() }) != 3){
			throw std::runtime_error("Wrong editor distance");
		}
	}
	{
		std::vector<long long> sequence1{ 2, 3, 5, 1 };
		std::vector<long long> sequence2{ 2, 1, 3, 4 };
		if (getEditorDistance(PackOfIterators<std::vector<long long>::iterator>
			{ sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end() }) != 4){
			throw std::runtime_error("Wrong editor distance");
		}
	}
}

int vagnerFisherEditorDistance(const std::vector<long long>& sequence1, const std::vector<long long>& sequence2){
	std::vector<std::vector<int> > VagnerFischer(sequence1.size() + 1, std::vector<int>(sequence2.size() + 1, 0));
	for (size_t i = 0; i <= sequence1.size(); ++i)
		VagnerFischer[i][0] = i;
	for (size_t j = 1; j <= sequence2.size(); ++j)
		VagnerFischer[0][j] = j;
	for (int i = 1; i <= sequence1.size(); ++i){
		for (int j = 1; j <= sequence2.size(); ++j){
			if (sequence1[i - 1] == sequence2[j - 1])
				VagnerFischer[i][j] = VagnerFischer[i - 1][j - 1];
			else{
				VagnerFischer[i][j] = std::min<int>(VagnerFischer[i - 1][j], VagnerFischer[i][j - 1]) + 1;
			}
		}
	}
	return VagnerFischer[sequence1.size()][sequence2.size()];
}
void checkingEditorDistance(){
	
	for (int test = 1; test <= TEST_VALUES; ++test){
		std::cerr << test << std::endl;
		std::vector<long long> sequence1 = generate_sequence_small();
		std::vector<long long> sequence2 = spoil_some_numbers(sequence1);
		int result = getEditorDistance(PackOfIterators<std::vector<long long>::iterator>
			{ sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end() });
		int correctAnswer = vagnerFisherEditorDistance(sequence1, sequence2);
		if (correctAnswer != result){
			throw std::logic_error("Difference haven't found correctly");
		}
	}
}

void unitCheckEditorPath(){
	{
		std::vector<long long> sequence1{ 1 };
		std::vector<long long> sequence2;
		PackOfIterators<std::vector<long long>::iterator> boundsOfContainers
		{ sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end() };
		std::vector<EditorState<long long> > editorScript = getEditorScript(boundsOfContainers);
		checkEditorCorrectness<long long>(boundsOfContainers, editorScript.begin(), editorScript.end());
	}
	{
		std::vector<long long> sequence1{ 1 };
		std::vector<long long> sequence2{ 2 };
		PackOfIterators<std::vector<long long>::iterator> boundsOfContainers
		{ sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end() };
		std::vector<EditorState<long long> > editorScript = getEditorScript(boundsOfContainers);
		checkEditorCorrectness<long long>(boundsOfContainers, editorScript.begin(), editorScript.end());
	}

	{
		std::vector<long long> sequence1{ 1, 2 };
		std::vector<long long> sequence2{ 2 };
		PackOfIterators<std::vector<long long>::iterator> boundsOfContainers
		{ sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end() };
		std::vector<EditorState<long long> > editorScript = getEditorScript(boundsOfContainers);
		checkEditorCorrectness<long long>(boundsOfContainers, editorScript.begin(), editorScript.end());
	}
	{
		std::vector<long long> sequence1{ 1, 2 };
		std::vector<long long> sequence2{ 2, 3 };
		PackOfIterators<std::vector<long long>::iterator> boundsOfContainers
		{ sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end() };
		std::vector<EditorState<long long> > editorScript = getEditorScript(boundsOfContainers);
		checkEditorCorrectness<long long>(boundsOfContainers, editorScript.begin(), editorScript.end());
	}
	{
		std::vector<long long> sequence1{ 1, 2 };
		std::vector<long long> sequence2{ 2, 3, 4 };
		PackOfIterators<std::vector<long long>::iterator> boundsOfContainers
		{ sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end() };
		std::vector<EditorState<long long> > editorScript = getEditorScript(boundsOfContainers);
		checkEditorCorrectness<long long>(boundsOfContainers, editorScript.begin(), editorScript.end());
	}
	{
		std::vector<long long> sequence1{ 2, 3, 5, 1 };
		std::vector<long long> sequence2{ 2, 3, 4 };
		PackOfIterators<std::vector<long long>::iterator> boundsOfContainers
		{ sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end() };
		std::vector<EditorState<long long> > editorScript = getEditorScript(boundsOfContainers);
		checkEditorCorrectness<long long>(boundsOfContainers, editorScript.begin(), editorScript.end());
	}
	{
		std::vector<long long> sequence1{ 2, 3, 5, 1 };
		std::vector<long long> sequence2{ 2, 1, 3, 4 };
		PackOfIterators<std::vector<long long>::iterator> boundsOfContainers
		{ sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end() };
		std::vector<EditorState<long long> > editorScript = getEditorScript(boundsOfContainers);
		checkEditorCorrectness<long long>(boundsOfContainers, editorScript.begin(), editorScript.end());
	}
}
void stressCheckEditorPath(){
	for (int tests = 1; tests <= TEST_VALUES; ++tests){
		std::cerr << tests << std::endl;
		std::vector<long long> sequence1 = generate_sequence();
		std::vector<long long> sequence2 = spoil_some_numbers_with_delete(sequence1);
		PackOfIterators<std::vector<long long>::iterator> boundsOfContainers
		{ sequence1.begin(), sequence1.end(), sequence2.begin(), sequence2.end() };
		std::vector<EditorState<long long> > editorScript = getEditorScript(boundsOfContainers);
		checkEditorCorrectness<long long>(boundsOfContainers, editorScript.begin(), editorScript.end());
	} 
}

void streamCheckEditorPath(){
	for (int tests = 1; tests <= TEST_VALUES; ++tests){
		std::cerr << tests << std::endl;
		generate_text();
		std::vector<std::string> strings1, strings2;
		std::vector<EditorState<std::string> > results = gettingChangesOfTwoFiles(strings1, strings2);
		
		
		checkEditorCorrectness<std::string>(PackOfIterators<std::vector<std::string>::iterator> 
			(strings1.begin(),  strings1.end(), strings2.begin(), strings2.end()), results.begin(), results.end());
	}
}
